#include <jni.h>
#include <cstring>
#include <string>
#include <iostream>
#include "utils/LogUtils.h"
#include "utils/AES.h"
#include "utils/encryption.h"
#include "utils/md5.h"
#include "utils/stock.h"
#include "utils/stock.cpp"
#include "utils/cutting.cpp"
#include "share.cpp"
#include <typeinfo>
#include <ctime>
#include <sys/stat.h>
#include <cstdio>
//#include "<android_log.h>"
#include <cerrno>
#include <unistd.h>
#include <sys/system_properties.h>

//额外附加的字符串
static const char *EXTRA_SIGNATURE = "zrh";
//效验签名
static int is_verify = 0;

// 客户端加密偏移量和模式
static const char *CLIENT_ENCODE_IV = "72389759823789";
static const char *CLIENT_ENCODE_MODE = "CFB";
// 服务器端加密偏移量和模式
static const char *SERVER_DECODE_IV = "21878437528934";
static const char *SERVER_DECODE_MODE = "CBC";

using namespace std;

void fistCheck(JNIEnv *env, jobject context);

extern "C" JNIEXPORT jstring JNICALL
Java_com_nolon_flange_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT void JNICALL
Java_n_Mediator_init(JNIEnv *env, jclass clazz, jobject context) {
    fistCheck(env, context);
}

/**
 * 判断是否首次运行，生成并保存信息
 */
void fistCheck(JNIEnv *env, jobject context) {
    jboolean fl = getBooleanSharedPreferences(env, context, env->NewStringUTF("fl"), JNI_TRUE);
    if (fl == JNI_TRUE) {
        jstring date = getCurrTime(env);
        setStringSharedPreference(env, context, env->NewStringUTF("date"), date);
        jstring info = genDeviceInfo(env);
        setStringSharedPreference(env, context, env->NewStringUTF("info"), info);
        jstring aid = getAndroidId(env, context);
        setStringSharedPreference(env, context, env->NewStringUTF("aid"), aid);
        jstring uuid = genUUID(env);
        setStringSharedPreference(env, context, env->NewStringUTF("uuid"), uuid);
        setBooleanSharedPreference(env, context, env->NewStringUTF("fl"), JNI_FALSE);

//        toast(env, context, env->NewStringUTF("init launch"));

        // 生成序列号
        std::string code;
        code.append(jstringTostring(env, date));
        code.append("|");
        code.append(jstringTostring(env, info));
        code.append("|");
        code.append(jstringTostring(env, aid));
        code.append("|");
        code.append(jstringTostring(env, uuid));

        const char *c_secret_key = APP_SIGNATURE;
        string encrypt_data = encryptByAES(code.c_str(), c_secret_key, CLIENT_ENCODE_IV,
                                           getModeByName(CLIENT_ENCODE_MODE));
        setStringSharedPreference(env, context, env->NewStringUTF("sn"),
                                  env->NewStringUTF(encrypt_data.c_str()));

        env->DeleteLocalRef(date);
        env->DeleteLocalRef(info);
        env->DeleteLocalRef(aid);
        env->DeleteLocalRef(uuid);
    }
}


/**
 * 对前台传输过来的ActiveCode字符串进行检验，从数据库提取SN
 * 返回比对结果，如果比对匹配，保存ActiveCode并设置isa = true
 */
extern "C" JNIEXPORT jboolean JNICALL
Java_n_Mediator_n0880(JNIEnv *env, jclass clazz, jobject context, jstring active_code) {
    if (env->GetStringLength(active_code) == 0) {
        return JNI_FALSE;
    }

    jstring serial_no = getStringSharedPreferences(env, context, CStr2Jstring(env, "sn"));

    const char *c_data1 = jstringTostring(env, active_code);
    const char *c_data2 = jstringTostring(env, serial_no);
    const char *c_secret_key = APP_SIGNATURE;

    // 解密两个字符串比较一下是否一致
    string data2 = decryptByAES(c_data1, c_secret_key, SERVER_DECODE_IV,
                                getModeByName(SERVER_DECODE_MODE));

    string data1 = decryptByAES(c_data2, c_secret_key, CLIENT_ENCODE_IV,
                                getModeByName(CLIENT_ENCODE_MODE));


    if (strcmp(data1.c_str(), data2.c_str()) == 0) {
        toast(env, context, CStr2Jstring(env, "比对成功!"));
        // 保存值和设置已经激活
        // 保存id，activecode,isactive
        jstring str_key_ac = CStr2Jstring(env, "ac");
        jstring str_key_a = CStr2Jstring(env, "isa");

        setStringSharedPreference(env, context, str_key_ac, active_code);
        setBooleanSharedPreference(env, context, str_key_a, JNI_TRUE);
        // 清理现场
        return true;
    }

    return false;
}

/**
 * 对比两个字符串的解密结果
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0888(JNIEnv *env, jclass clazz, jobject context, jstring param1,
                      jstring param2) {
    const char *c_data1 = env->GetStringUTFChars(param1, 0);
    const char *c_data2 = env->GetStringUTFChars(param2, 0);
    const char *c_secret_key = APP_SIGNATURE;

    string data1 = decryptByAES(c_data1, c_secret_key, CLIENT_ENCODE_IV,
                                getModeByName(CLIENT_ENCODE_MODE));

    string data2 = decryptByAES(c_data2, c_secret_key, SERVER_DECODE_IV,
                                getModeByName(SERVER_DECODE_MODE));

    return env->NewStringUTF(data1.append("\n").append(data2).c_str());
}


/**
 * 对客户发过来的id进行解密再加密
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0005(JNIEnv *env, jclass clazz, jstring data,
                      jstring secret_key, jstring iv,
                      jstring i_mode) {
    const char *c_data = env->GetStringUTFChars(data, 0);
//    const char *c_secret_key = env->GetStringUTFChars(secret_key, 0);
    const char *c_secret_key = APP_SIGNATURE;
    const char *c_iv = env->GetStringUTFChars(iv, 0);
    const char *c_mode = env->GetStringUTFChars(i_mode, 0);
    string encrypt_data = encryptByAES(c_data, c_secret_key, c_iv, getModeByName(c_mode));
    return env->NewStringUTF(encrypt_data.c_str());
}

/**
 * Server端对加密SN进行解密再加密,和上面方法比对留一个
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0006(JNIEnv *env, jclass clazz, jstring data) {
    const char *c_data = env->GetStringUTFChars(data, 0);
    const char *c_secret_key = APP_SIGNATURE;
    string decrypt_data = decryptByAES(c_data, c_secret_key, CLIENT_ENCODE_IV,
                                       getModeByName(CLIENT_ENCODE_MODE));
    string encrypt_data = encryptByAES(decrypt_data.c_str(), c_secret_key, SERVER_DECODE_IV,
                                       getModeByName(SERVER_DECODE_MODE));
    return env->NewStringUTF(encrypt_data.c_str());
}

/**
 * AES解密
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0009(JNIEnv *env, jclass clazz, jstring data,
                      jstring secret_key, jstring iv,
                      jstring i_mode) {
    const char *c_data = env->GetStringUTFChars(data, 0);
    const char *c_secret_key = APP_SIGNATURE;
//    const char *c_secret_key = env->GetStringUTFChars(secret_key, 0);
    const char *c_iv = env->GetStringUTFChars(iv, 0);
    const char *c_mode = env->GetStringUTFChars(i_mode, 0);
    string decrypt_data = decryptByAES(c_data, c_secret_key, c_iv, getModeByName(c_mode));
    return env->NewStringUTF(decrypt_data.c_str());
}

/**
 * Client段对未加密的SN字符串进行AES加密
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0020(JNIEnv *env, jclass clazz, jstring data) {
    const char *c_data = env->GetStringUTFChars(data, 0);
    const char *c_secret_key = APP_SIGNATURE;
    string decrypt_data = decryptByAES(c_data, c_secret_key, CLIENT_ENCODE_IV,
                                       getModeByName(CLIENT_ENCODE_MODE));
    return env->NewStringUTF(decrypt_data.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_n_MD5Utlis_00024Companion_signatureParams(JNIEnv *env, jobject thiz,
                                               jstring params_) {
    if (is_verify == 0) {
        return env->NewStringUTF("error_signature");
    }
    const char *params = env->GetStringUTFChars(params_, 0);
    //MD5 签名规则，加点料
    //1、字符前面加点料
    string signature_str(params);
    signature_str.insert(0, EXTRA_SIGNATURE);
    //2、后面去掉两位
    signature_str = signature_str.substr(0, signature_str.length() - 2);

//    __android_log_print(ANDROID_LOG_ERROR, "this", "signature_str: %s", signature_str.c_str());

    //3、md5加密
    MD5 *ctx = new MD5(signature_str);
//    __android_log_print(ANDROID_LOG_ERROR, "this", "MD5 2: %s", ctx->toStr().c_str());

    env->ReleaseStringUTFChars(params_, params);

    return env->NewStringUTF(ctx->toStr().c_str());
}

extern "C" JNIEXPORT void JNICALL
Java_n_MD5Utlis_00024Companion_signatureVerify(JNIEnv *env, jobject thiz,
                                               jobject contenxt) {
    //1、获取包名；
    jclass j_clz = env->GetObjectClass(contenxt);
    jmethodID j_mid = env->GetMethodID(j_clz, "getPackageName", "()Ljava/lang/String;");
    jstring j_package_name = (jstring) env->CallObjectMethod(contenxt, j_mid);

    //2、比对包名是否一样；
    const char *c_package_name = env->GetStringUTFChars(j_package_name, NULL);
    if (strcmp(c_package_name, PACKAGE_NAME) != 0) {
        return;
    }
//    __android_log_print(ANDROID_LOG_ERROR, "this", "包名一致：%s", c_package_name);

    //3、获取签名
    //3.1、获取PackageManager
    j_mid = env->GetMethodID(j_clz, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    jobject pack_manager = env->CallObjectMethod(contenxt, j_mid);
    //3.2、获取PackageInfo
    j_clz = env->GetObjectClass(pack_manager);
    j_mid = env->GetMethodID(j_clz, "getPackageInfo",
                             "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jobject package_info = env->CallObjectMethod(pack_manager, j_mid, j_package_name, 64);
    //3.3、获取signatures 数组
    j_clz = env->GetObjectClass(package_info);
    jfieldID j_fid = env->GetFieldID(j_clz, "signatures", "[Landroid/content/pm/Signature;");
    jobjectArray signatures = (jobjectArray) env->GetObjectField(package_info, j_fid);
    //3.4、获取signatures[0]
    jobject signatures_first = env->GetObjectArrayElement(signatures, 0);
    //3.5、调用signatures[0].toCharsString()
    j_clz = env->GetObjectClass(signatures_first);
    j_mid = env->GetMethodID(j_clz, "toCharsString", "()Ljava/lang/String;");
    jstring j_signature_str = (jstring) env->CallObjectMethod(signatures_first, j_mid);
    const char *c_signature_str = env->GetStringUTFChars(j_signature_str, NULL);
    //4、比对签名是否一样；
    if (strcmp(c_signature_str, APP_SIGNATURE) != 0) {
        return;
    }
//    __android_log_print(ANDROID_LOG_ERROR, "this", "签名校验成功：%s", c_signature_str);
    //签名验证成功
    is_verify = 1;

}

/**
 * 对比字符串解密后是否吻合,如果吻合则保存激活结果;
 * [param1] 已客户端加密
 * [param2] 用server端进行加密
 *
 */

/**
 * 获取SN的值
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0016(JNIEnv *env, jclass clazz, jobject context) {
    return getStringSharedPreferences(env, context, env->NewStringUTF("sn"));
}



extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0010(JNIEnv *env, jclass clazz, jobject context) {
    return CStr2Jstring(env, "");
}

extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0015(JNIEnv *env, jclass clazz, jobject context) {
    return CStr2Jstring(env, "");
}
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0011(JNIEnv *env, jclass clazz, jobject context) {
    return CStr2Jstring(env, "");
}
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0012(JNIEnv *env, jclass clazz, jobject context) {
    return CStr2Jstring(env, "");
}
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0013(JNIEnv *env, jclass clazz, jobject context) {
    return CStr2Jstring(env, "");
}
extern "C" JNIEXPORT jstring JNICALL
Java_n_Mediator_n0014(JNIEnv *env, jclass clazz, jobject context) {
    return env->NewStringUTF("");
}



extern "C" JNIEXPORT jintArray JNICALL
Java_n_Init_n0099(JNIEnv *env, jclass clazz, jobject context,
                  jintArray lengths,
                  jintArray counts, jint raw_len, jint raw_max_combination) {
    // 判断签名是否一致
    jboolean check_signature = verifySignatures(env, context);
    if (check_signature != JNI_TRUE) {
        setIntSharedPreference(env, context, CStr2Jstring(env, "risk"), 5);
//        return nullptr;// 抛空指针造成闪退
    }
    jboolean isa = getBooleanSharedPreferences(env, context, CStr2Jstring(env, "isa"), JNI_FALSE);

    jint *jLens = env->GetIntArrayElements(lengths, 0);
    jint *jCounts = env->GetIntArrayElements(counts, 0);
    int arrayLen = (int) env->GetArrayLength(lengths);
    int cLens[arrayLen];
    int cCounts[arrayLen];
    for (int i = 0; i < arrayLen; ++i) {
        cLens[i] = (int) jLens[i];
        cCounts[i] = (int) jCounts[i];
    }
    int cRawLen = (int) raw_len;
    int cComb = (int) raw_max_combination;

    if (!isa) {
//        toast(env,context,CStr2Jstring(env,"未激活"));
        auto *simple_planner = new SimpleCuttingPlanner(arrayLen, cLens, cCounts, cRawLen, cComb);
        int *simple_s = simple_planner->solve();
        int simple_length = simple_planner->getLen();
        jintArray simple_solution = env->NewIntArray((jsize) simple_length);
        jint simple_t[simple_length];
        for (int i = 0; i < simple_length; ++i) {
            simple_t[i] = (jint) *(simple_s + i);
        }

        env->SetIntArrayRegion(simple_solution, 0, simple_length, simple_t);
        return simple_solution;
    }

    auto *planner = new CuttingPlanner(arrayLen, cLens, cCounts, cRawLen, cComb);

    int *s = planner->solve(100);
    int length = planner->getLen();

    jintArray solution = env->NewIntArray((jsize) length);
    jint t[length];
    for (int i = 0; i < length; ++i) {
        t[i] = (jint) *(s + i);
    }

    env->SetIntArrayRegion(solution, 0, length, t);
    return solution;
}


/**
 * 生成Date加入SP
 */
extern "C" JNIEXPORT void JNICALL
Java_n_Init_n0015(JNIEnv *env, jclass clazz, jobject context) {
    jstring date = getCurrTime(env);
    setStringSharedPreference(env, context, CStr2Jstring(env, "date"), date);
}

/**
 * 生成Info加入SP
 */
extern "C" JNIEXPORT void JNICALL
Java_n_Init_n0088(JNIEnv *env, jclass clazz, jobject context) {
    jstring info = genDeviceInfo(env);
    setStringSharedPreference(env, context, CStr2Jstring(env, "info"), info);
}

/**
 * 获取android id加入到SP中
 */
extern "C" JNIEXPORT void JNICALL
Java_n_Init_n0708(JNIEnv *env, jclass clazz, jobject context) {
    jstring aid = getAndroidId(env, context);
    setStringSharedPreference(env, context, CStr2Jstring(env, "aid"), aid);
}

/**
 * 生成UUID加入SP
 */
extern "C" JNIEXPORT void JNICALL
Java_n_Init_n0808(JNIEnv *env, jclass clazz, jobject context) {
    jstring uuid = genUUID(env);
    setStringSharedPreference(env, context, CStr2Jstring(env, "uuid"), uuid);
}

/**
 * 初步判断是否已经激活
 */
extern "C" JNIEXPORT jboolean JNICALL
Java_n_Init_isActive(JNIEnv *env, jclass clazz, jobject context) {
    jboolean isa = getBooleanSharedPreferences(env, context, CStr2Jstring(env, "isa"), JNI_FALSE);
    return isa;
}

/**
 * 获取SP中的加密id值
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Init_n0020(JNIEnv *env, jclass clazz, jobject context) {
    jclass cls_PreferenceManager = env->FindClass("androidx/preference/PreferenceManager");
    jmethodID mid_getString = env->GetStaticMethodID(cls_PreferenceManager,
                                                     "getDefaultSharedPreferences",
                                                     "(Landroid/content/Context;)Landroid/content/SharedPreferences;");
    jobject cls_sp = env->CallStaticObjectMethod(cls_PreferenceManager, mid_getString, context);
    jclass cls_SharedPreferences = env->FindClass("android/content/SharedPreferences");
    jmethodID med_getString = env->GetMethodID(cls_SharedPreferences, "getString",
                                               "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
    jstring id = CStr2Jstring(env, "sn");
    jstring empty = CStr2Jstring(env, "");
    auto str_id = (jstring) env->CallObjectMethod(cls_sp, med_getString, id, empty);

    env->DeleteLocalRef(cls_SharedPreferences);
    env->DeleteLocalRef(cls_PreferenceManager);
    env->DeleteLocalRef(cls_sp);
    env->DeleteLocalRef(id);
    env->DeleteLocalRef(empty);

    return str_id;
}
/**
 * 获取SP中的加密 active code
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Init_n0040(JNIEnv *env, jclass clazz, jobject context) {
    return getStringSharedPreferences(env, context, env->NewStringUTF("ac"));
}

/**
 * 获取SP中的 Phone
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Init_n0060(JNIEnv *env, jclass clazz, jobject context) {
    return getStringSharedPreferences(env, context, env->NewStringUTF("phone"));
}

/**
 * 获取SP中的 Date
 */
extern "C" JNIEXPORT jstring JNICALL
Java_n_Init_n0045(JNIEnv *env, jclass clazz, jobject context) {
    return getStringSharedPreferences(env, context, env->NewStringUTF("date"));
}
